#include "Socket.h"
#include <errno.h> 
#include "CinDataMap.h"

Socket::Socket() : m_sock(-1)
{

    memset(&m_addr, 0, sizeof(m_addr));
}

Socket::~Socket()
{
    if (is_valid())
        ::close(m_sock);
}

void Socket::closeFd(int fd)
{
  ::close(fd);
}

bool Socket::create(IObserver *ob)
{
    m_sock = socket(PF_INET, SOCK_STREAM, 0);

    if (!is_valid())
        return false;

    std::cout << m_sock << "  Socket::create() \n";
    // TIME_WAIT - argh
    // int on = 1;
    // if ( setsockopt ( m_sock, SOL_SOCKET, SO_REUSEADDR, ( const char* ) &on, sizeof ( on ) ) == -1 )
    //     return false;
    this->mObserver = ob;
    return true;
}

bool Socket::bind(const int port)
{

    if (!is_valid())
    {
        return false;
    }

    m_addr.sin_family = PF_INET;
    m_addr.sin_addr.s_addr = INADDR_ANY;
    m_addr.sin_port = htons(port);

    int bind_return = ::bind(m_sock, (struct sockaddr *)&m_addr, sizeof(m_addr));

    std::cout << m_sock << "  Socket::bind\n";
    if (bind_return == -1)
    {
        return false;
    }

    return true;
}

int Socket::listen() const
{
    if (!is_valid())
    {
        return -1;
    }

    int listen_return = ::listen(m_sock, MAXCONNECTIONS);

    return m_sock;
}

int Socket::accept(int fd) const
{
    int addr_length = sizeof(m_addr);
    int peerfd = ::accept(fd, (sockaddr *)&m_addr, (socklen_t *)&addr_length);

    return peerfd;
}

int Socket::send(int fd, const std::string s) const
{
    int slen = ::send(fd, s.c_str(), s.size(), MSG_NOSIGNAL);
    return slen;
}
bool Socket::send(int fd,  unsigned short nOpcode, const char* pDataBuffer, const unsigned int& nDataSize) 
{
    const unsigned short nSendSize = nDataSize + sizeof(NetPacketHeader);
    NetPacketHeader* pHead = (NetPacketHeader*) malloc(nSendSize);
	//NetPacketHeader* pHead = (NetPacketHeader*) m_cbSendBuf;
	pHead->wOpcode = nOpcode;
    pHead->wDataSize = nSendSize;
	
	if ( (nDataSize > 0) && (pDataBuffer != 0) )
	{
		memcpy(pHead+1, pDataBuffer, nDataSize);
	}

   // cout<< "nSendSize= "<< nSendSize << " nDataSize=" <<nDataSize<< endl;
	//int ret = ::send(fd, m_cbSendBuf, nSendSize, MSG_NOSIGNAL);
    int ret = writen(fd, (const char*)pHead, nSendSize);
   // m_cbSendBuf[0] = '\0';
    free(pHead);
	return (ret > 0) ? true : false;
}

int Socket::writen(int fd, const char* msg, int size) const
{
    const char* buf = msg;
    int count = size;
    while (count > 0)
    {
        int len = ::send(fd, buf, count, MSG_NOSIGNAL);
        if (len == -1)
        {
            //close(fd);
            return -1;
        }
        else if (len == 0)
        {
            continue;
        }
        buf += len;
        count -= len;
    }
    return size;
}

int Socket::readn(int fd, char* buf, int size) const
{
    char* pt = buf;
    int count = size;
    int revCnt = 0;
    bool isClose = false;
    while (count > 0)
    {
        int len = ::recv(fd, pt, count, MSG_NOSIGNAL);
        int sockErr = errno;      
        std::cout << "len ==" << len << "  count=" <<count << " sockErr =" << sockErr<< endl;
        revCnt+=len;
        if (len == -1)
        {
            if(sockErr == EWOULDBLOCK){
              return revCnt+1;
           }else{
              return -2;
           }           
        }
        else if (len == 0)
        {
           if(sockErr == EWOULDBLOCK){
              return -2;
           }else{
              return size - count;
           }
           
        }
        pt += len;
        count -= len;
    }
    
    return size;
}


int Socket::recv(int fd, std::string &s) const
{

    char buf[MAXRECV + 1];

    s = "";

    memset(buf, 0, MAXRECV + 1);

    int status = ::recv(fd, buf, MAXRECV, 0);

    if (status == -1)
    {
        std::cout << "status == -1   errno == " << errno << "  in Socket::recv\n";
        return 0;
    }
    else if (status == 0)
    {
        return 0;
    }
    else
    {
        s = buf;
        return status;
    }
}

int Socket::parseRData(int fd)
{
   DataItem dataItem = CinDataMap::GetInstance()->findDataByid(fd);
   int rsize = dataItem.m_nRecvSize;
   cout<< "parseRData dataItem rsize= "<< rsize << endl;
   if (rsize > 0)
   {       
       memcpy(m_cbDataBuf, dataItem.m_cbDataBuf, rsize);
       memcpy(m_cbDataBuf+rsize, m_cbRecvBuf, m_nRecvSize);
       m_nRecvSize += rsize;
       CinDataMap::GetInstance()->removeData(fd);
   }else
   {
       memcpy(m_cbDataBuf, m_cbRecvBuf, m_nRecvSize);
   }
    unsigned short len = sizeof(NetPacketHeader);
    while (m_nRecvSize > len) {
        NetPacketHeader *pHead = (NetPacketHeader*)(m_cbDataBuf);
        unsigned short nPacketSize = pHead->wDataSize;
        unsigned short wOpcode = pHead->wOpcode;
        std::cout <<"nPacketSize=="<<nPacketSize << " wOpcode =" << wOpcode<<" rData.size="<<m_nRecvSize << endl;
        if(m_nRecvSize >= nPacketSize && wOpcode > 0){
            const unsigned short nDataSize = nPacketSize - len;            
            char *buf = (char*)malloc(nDataSize);
            memcpy(buf, m_cbDataBuf+len, nDataSize);
			memmove(m_cbDataBuf, m_cbDataBuf+nPacketSize, m_nRecvSize);
            m_nRecvSize -= nPacketSize;
            if(m_nRecvSize < 0) m_nRecvSize =0;

            std::cout <<" 2 nDataSize=="<<nDataSize <<" rData.size="<<m_nRecvSize<< endl;
            this->mObserver->RevData(fd, wOpcode, buf, nDataSize);
            //free(buf);
        }else
        {
            saveDataItem(fd);
            break;
        }
    }
    return 1;
}

int Socket::doRecv(int fd)
{
   int  nFlag = 1;
   
   m_nRecvSize = readn(fd,	m_cbRecvBuf, 8196);
   cout<< "doRecv m_nRecvSize = "<< m_nRecvSize << endl;
   if (m_nRecvSize > 0)
   {
      parseRData(fd);
   }else{
      nFlag = m_nRecvSize;
   }  
  
   return nFlag;
}

void Socket::saveDataItem(int fd)
{
    if(m_nRecvSize> 0){// 保存收到的数据
        DataItem dataItem;
        dataItem.m_nRecvSize = m_nRecvSize;
        memcpy(dataItem.m_cbDataBuf, m_cbDataBuf, m_nRecvSize);
        CinDataMap::GetInstance()->addData(fd, dataItem);
        cout<< " save dataItem= "<< dataItem.m_nRecvSize << endl;
    }
}

void Socket::set_non_blocking(int fd, const bool b)
{

    int opts;

    opts = fcntl(fd, F_GETFL);

    if (opts < 0)
    {
        return;
    }

    if (b)
        opts = (opts | O_NONBLOCK);
    else
        opts = (opts & ~O_NONBLOCK);

    fcntl(fd, F_SETFL, opts);
}
